﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using WatiN.Core;
using System.IO;

using WebAutoUtils;
using WebAutomation.Browsers;
using WebAutomation.Browsers.Chrome;
using System.ComponentModel;

namespace WebAutomation
{
    public class TestCommands
    {

        private static WebBrowserObject _browserObject;
        private string _commandName;
        private string[] _commandParameters;
        public static DataSet _dataFromDB = new DataSet();
        public static Dictionary<string, DataTable> _dataVariables = new Dictionary<string, DataTable>();
        public static Dictionary<string, string> ListOfVariables = new Dictionary<string, string>();
        public static string Url="";
        private static ManualResetEvent evt = new ManualResetEvent(true);
        private static bool isWaiting = false;
        private static Thread _thread;
        public static TestLineResult TestLineResult = new TestLineResult();
        private static string _previousDataSetName = "";
        private static string _testName = "";
        private static string _testFileName = "";

        public TestCommands(WebBrowserObject browserObject, string commandName, string[] commandParameters,string testName,string testFileName)
        {
            _browserObject = browserObject;
            _commandName = commandName;
            _commandParameters = commandParameters;
            _testName = testName;
            _testFileName = testFileName;

        }

        public static void OnPause()
        {
            isWaiting = true;
            evt.Reset(); 
        }

        public static void OnResume()
        {
            isWaiting = false;
                evt.Set(); 
        }

        public static void RunToCompletion()
        {
            isWaiting = false;
            evt.Set(); 
        }

        public static void OnStop()
        {
            isWaiting = false;
            _thread.Join();

        }

        public static void DisposeVariables()
        {
            _dataFromDB = new DataSet();
            _dataVariables = new Dictionary<string, DataTable>();
            ListOfVariables = new Dictionary<string, string>();
        }

        public static ICommand GetTestCommand(string commandName, WebBrowserObject BrowserObject=null)
        {
            ICommand iCommand = null;
            switch (commandName.ToLower())
            {
                case "browser":
                    iCommand = new BrowserCommand(BrowserObject);
                    break;
                case "textbox":
                    iCommand = new TextBoxCommand(BrowserObject);
                    break;
                case "button":
                    iCommand = new ButtonCommand(BrowserObject);
                    break;
                case "waituntil":
                    iCommand = new WaitUntilCommand(BrowserObject);
                    break;
                case "element":
                    iCommand = new ElementCommand(BrowserObject);
                    break;
                case "link":
                    iCommand = new LinkCommand(BrowserObject);
                    break;
                case "picture":
                    iCommand = new PictureCommand(BrowserObject);
                    break;
                case "data":
                    iCommand = new DataAdapterCommand(BrowserObject);
                    break;
                case "variable":
                    iCommand = new VariableCommand(BrowserObject);
                    break;
                case "endtest":
                    iCommand = new EndTestCommand(BrowserObject);
                    break;
                case "assert":
                    iCommand = new AssertCommand(BrowserObject);
                    break;
                case "script":
                    iCommand = new ScriptCommand(BrowserObject);
                    break;
                case "app":
                    iCommand = new AppCommand(BrowserObject);
                    break;
                case "test":
                    iCommand = new TestCommand(BrowserObject);
                    break;
                case "io":
                    iCommand = new IOCommand(BrowserObject);
                    break;
                
                
            }
            if (iCommand==null)
            {
                throw new Exception("The Command (" + commandName + ") is not recognized.");
            }
            return iCommand;
        }

        public static bool DoExecuteTestCommand(ICommand iCommand, WebBrowserObject browserObject, string[] commandParameters, string url, string testRepo, string testResultLocation, string dbConnString, ref DataSet dataFromDB, ref TestLineResult testLineResult, ref bool stopTest, WebAutoUtils.ITestDataExceptionRules testDataExceptionRules)
        {
            bool result = iCommand.ExecuteCommand(browserObject, commandParameters, url, testRepo, testResultLocation, dbConnString, ref dataFromDB, ref testLineResult, ref stopTest,testDataExceptionRules);
            if (_dataFromDB.Tables.Count > 0)
            {
                _createInMemoryDataVariables(_dataFromDB);
            }
            return result;
        }

        public TestLineResult ExecuteTestCommand(string url, string testRepo, string testResultLocation, string dbConnString, ref bool stopTest, WebAuto.OnTestCommandStart onTestCommandStart, WebAuto.OnTestCommandCompleted onTestCommandCompleted, Thread thread, int lineNumber, WebAutoUtils.ITestDataExceptionRules testDataExceptionRules)
        {

            var pattern = new Regex(
                                  @"( [^\W_\d]              # starting with a letter
                                                            # followed by a run of either...
                                      ( [^\W_\d] |          #   more letters or
                                        [-'\d](?=[^\W_\d])  #   ', -, or digit followed by a letter
                                      )*
                                      [^\W_\d]              # and finishing with a letter
                                    )",
                                  RegexOptions.IgnorePatternWhitespace);

            _thread = thread;
            ICommand iCommand = GetTestCommand(_commandName);

            TestLineResult testLineResult = new TestLineResult();
            try
            {
                if (_commandParameters[1].Contains("Data[") && !_commandParameters[1].Contains("Variable["))
                {
                    
                    string variableData = _getDataVariable(_commandParameters);
                    if (variableData.Contains("Error."))
                    {
                        testLineResult.LinePassed = false;
                        testLineResult.LineCommand = _commandParameters[0];
                        testLineResult.LineMessage = variableData;
                    }
                    _commandParameters[1] = variableData;
                }
                if (_commandParameters[1].Contains("Variable[") && !_commandParameters[1].Contains("Data["))
                {
                    

                            var input = _commandParameters[1];
                            var listOfWords = pattern.Matches(input);
                            string[] arrayOfWords = new string[listOfWords.Count];
                            int i = 0;
                            foreach (Match m in listOfWords)
                            {
                                arrayOfWords[i] = m.Groups[1].Value;
                                i++;
                            }
                            List<string> listOfVariables = new List<string>();
                            i = 0;
                            foreach(var item in arrayOfWords)
                            {
                                if(item=="Variable")
                                {

                                    listOfVariables.Add(arrayOfWords[i + 1]);
                                }
                                i++;
                            }
                        
                            foreach(var item in listOfVariables)
                            {
                                string variableData = GetVariable(item);
                                _commandParameters[1] =_commandParameters[1].Replace(item, variableData);
                            }

                            _commandParameters[1] =_commandParameters[1].Replace("Variable[", "").Replace("]", "");
                        

                    //string varName = _commandParameters[1].Replace("Variable[", "").Replace("]", "");
                    //if (!varName.Contains(":[") || !varName.Contains(":") || !varName.Contains("["))
                    //{
                    //    string origVariableData = GetVariable(varName);
                    //    string variableData = origVariableData;

                    //    _commandParameters[1] = variableData;
                    //}
                }
            }catch{}

            bool hasCommandStartDelegate = onTestCommandStart!=null;
            bool hasCommandCompletedDelegate = onTestCommandCompleted != null;
            Url = url;
            if (hasCommandStartDelegate)
            {
                onTestCommandStart.Invoke(_commandParameters, lineNumber, thread, _browserObject);
                while (isWaiting)
                {

                    Thread.Sleep(2000);
                }
            }
            DoExecuteTestCommand(iCommand, _browserObject, _commandParameters, url, testRepo, testResultLocation,
                                 dbConnString, ref _dataFromDB, ref testLineResult, ref stopTest,testDataExceptionRules);

            testLineResult.LineSubCommand = iCommand.GetType().ToString().Replace("WebAutomation.", "") + ":" + testLineResult.LineSubCommand;

            if (hasCommandCompletedDelegate)
            {
                onTestCommandCompleted.Invoke(_commandParameters, lineNumber, thread, _browserObject);
                while (isWaiting)
                {

                    Thread.Sleep(2000);
                }
            }
            TestLineResult = testLineResult;
            return testLineResult;
        }

        public static string GetDataVariable(string dataSetName,int rowNumber,int colNumber,int recordSetNumber)
        {
            string returnValue = "";
            string dataKeyPartName = _getDataKeyPartName(dataSetName);
            try
            {
                
                DataTable dt = _dataVariables[recordSetNumber + "_" + dataKeyPartName];
                DataRow dr = dt.Rows[rowNumber];

                returnValue = dr[colNumber].ToString();
            }catch(Exception ex)
            {
                string extraMessage = "";
                if (ex.Message.Contains("The given key was not present"))
                {
                    extraMessage = ".This usually means that the recordset number(" + recordSetNumber + ") specified in your test does not exist from the SQL that was executed.Check your SQL script and ensure that it is returning the correct number of recordsets.";
                }
                returnValue = "Error.(" + recordSetNumber + "_" + dataKeyPartName + ")" + ex.Message + extraMessage;
            }
            return returnValue;
        }

        private static string _getDataKeyPartName(string dataSetName)
        {
            int testId = WebAuto.TestId;
            string returnValue = dataSetName + _testName + _testFileName + "_" + testId;
          

            return returnValue;
        }


        private static void _createInMemoryDataVariables(DataSet dataFromDB)
        {

            string dataKeyPartName = _getDataKeyPartName(dataFromDB.DataSetName);
           

            int count = 0;
            
            foreach(DataTable dt in dataFromDB.Tables)
            {
                string name = count + "_" + dataKeyPartName;
                if (!_dataVariables.ContainsKey(name))
                {
                    _dataVariables.Add(name, dt);
                }
                count++;
            }

        }

        private string _getDataVariable(string[] _commandParameters)
        {
            char splitChar=':';
            string origData = _commandParameters[1];
            if(origData.Contains(":"))
            {
                splitChar = ':';
            }
            if (origData.Contains(";"))
            {
                splitChar = ';';
            }
            if (origData.Contains("|"))
            {
                splitChar = '|';
            }
            string[] origDataArray = origData.Split(splitChar);
            string name = origDataArray[0];
            string value = origDataArray[1];
            string[] data = value.Replace("Data[", "").Replace("]", "").Split(',');
            string dataSetName = data[0];
            int rowNumber = Convert.ToInt32(data[1]);
            int colNumber = Convert.ToInt32(data[2]);
            int recordSetNumber = 0;
            try
            {
                recordSetNumber = Convert.ToInt32(data[3]);
            }
            catch { }
            string variableValue = GetDataVariable(dataSetName, rowNumber, colNumber, recordSetNumber);

            string recombindedValue = name + splitChar + variableValue;

            return recombindedValue;
        }

        public static void SetVariable(string variableName,string variableValue)
        {
            if(!ListOfVariables.ContainsKey(variableName))
            {
                ListOfVariables.Add(variableName, variableValue);
            }else
            {
                ListOfVariables[variableName] = variableValue;
            }

        }

        public static string GetVariable(string variableName)
        {
            string returnValue = "";
            bool containsColon = false;
            string eleIDOrName = "";
            if (variableName.Contains(":"))
            {
                eleIDOrName = variableName.Split(':')[0];
                variableName = variableName.Split(':')[1];
                containsColon = true;
            }
            if (variableName.StartsWith("'") && variableName.EndsWith("'"))
            {
                variableName = variableName.Replace("'", "");
            }
            if (ListOfVariables.ContainsKey(variableName))
            {
                returnValue = ListOfVariables[variableName];
            }
            if (containsColon)
            {
                returnValue = eleIDOrName + ":" + returnValue;
            }

            return returnValue;
        }


        public static string GetBrowserType(ref string processName, ref string processToKill, WebBrowserObject browser=null,string browserName="")
        {
            string returnValue = "IE";
            processName = "IEXPLORE";
            processToKill = "IExplore.exe";
            if (!string.IsNullOrEmpty(browserName))
            {

                if (browserName.ToLower() == "chromebrowser" || browserName.ToLower() == "chrome")
                {
                    processName = "chrome";
                    processToKill = "chrome.exe";
                    returnValue = "Chrome";
                }

                if (browserName.ToLower() == "firefoxbrowser" || browserName.ToLower() == "firefox")
                {
                    processName = "firefox";
                    processToKill = "firefox.exe";
                    returnValue = "FireFox";
                }
                if (browserName.ToLower() == "safaribrowser" || browserName.ToLower() == "safari")
                {
                    processName = "safari";
                    processToKill = "safari.exe";
                    returnValue = "Safari";
                }
            }
            else
            {
                WebBrowserObject brow = _browserObject;

                if (browser != null)
                {
                    brow = browser;
                }
                if (brow.GetType().FullName.Contains("ChromeBrowser"))
                {
                    processName = "chrome";
                    processToKill = "chrome.exe";
                    returnValue = "Chrome";
                }

                if (brow.GetType().FullName.Contains("FireFoxBrowser"))
                {
                    processName = "firefox";
                    processToKill = "firefox.exe";
                    returnValue = "FireFox";
                }

                if (brow.GetType().FullName.Contains("SafariBrowser"))
                {
                    processName = "safari";
                    processToKill = "safari.exe";
                    returnValue = "Safari";
                }
            }

            return returnValue;
        }


        public static bool KillBrowserProcess(bool openBlankBrowser, string browser)
        {
            string processName = "IEXPLORE";
            string processToKill = "IExplore.exe";
            GetBrowserType(ref processName, ref processToKill,null, browser);

            bool returnValue = false;
            try
            {
                System.Diagnostics.Process[] listOfBrowsers =
                        System.Diagnostics.Process.GetProcessesByName(processName);
                if (listOfBrowsers.Count() == 0)
                {
                    listOfBrowsers =
                        System.Diagnostics.Process.GetProcessesByName(processName.ToLower());
                }
                if (listOfBrowsers.Count() == 0)
                {
                    listOfBrowsers =
                        System.Diagnostics.Process.GetProcesses();
                }

                foreach (var item in listOfBrowsers)
                {
                    if (item.ProcessName.ToLower() == processName.ToLower())
                    {
                        item.Kill();
                    }
                }

                if (openBlankBrowser)
                {
                    ProcessStartInfo startInfo = new ProcessStartInfo(processToKill);
                    startInfo.Arguments = "about:blank";
                    Process.Start(startInfo);
                }
            }
            catch (Win32Exception win32EX)
            {
                returnValue = true;
            }
            catch
            {
                returnValue = true;
            }

            return returnValue;
        }


        public static bool KillBrowserProcess(bool openBlankBrowser,WebBrowserObject browser)
        {
            string processName = "IEXPLORE";
            string processToKill = "IExplore.exe";
            GetBrowserType(ref processName, ref processToKill, browser);

            bool returnValue = false;
            try
            {
                System.Diagnostics.Process[] listOfBrowsers =
                        System.Diagnostics.Process.GetProcessesByName(processName);
                if (listOfBrowsers.Count()==0)
                {
                    listOfBrowsers =
                        System.Diagnostics.Process.GetProcessesByName(processName.ToLower());
                }
                if (listOfBrowsers.Count() == 0)
                {
                    listOfBrowsers =
                        System.Diagnostics.Process.GetProcesses();
                }

                foreach (var item in listOfBrowsers)
                {
                    if (item.ProcessName.ToLower() == processName.ToLower())
                    {
                        item.Kill();
                    }
                }

                if (openBlankBrowser)
                {
                    ProcessStartInfo startInfo = new ProcessStartInfo(processToKill);
                    startInfo.Arguments = "about:blank";
                    Process.Start(startInfo);
                }
            }
            catch (Win32Exception win32EX)
            {
                returnValue = true;
            }
            catch
            {
                returnValue = true;
            }

            return returnValue;
        }


        public static bool KillBrowserProcess(int processId, bool openBlankBrowser)
        {
            string processName = "IEXPLORER";
            string processToKill = "IExplore.exe";
            GetBrowserType(ref processName, ref processToKill);
          
            bool returnValue = false;
            int pId = processId;
            try
            {
                System.Diagnostics.Process procs = System.Diagnostics.Process.GetProcessById(pId);
                if (procs != null)
                {
                    procs.Kill();
                    if (openBlankBrowser)
                    {
                        System.Diagnostics.Process[] listOfBrowsers =
                            System.Diagnostics.Process.GetProcessesByName(processName);
                        if (!listOfBrowsers.Any())
                        {
                            ProcessStartInfo startInfo = new ProcessStartInfo(processToKill);
                            startInfo.Arguments = "about:blank";
                            Process.Start(startInfo);
                        }
                    }
                    returnValue = true;
                }
            }catch(Win32Exception win32EX)
            {
                returnValue = true;
            }catch
            {
                returnValue = true;
            }

            return returnValue;
        }

        public static bool GoToURLInBrowser(string url)
        {
            bool returnValue = false;
            try
            {
                string processName = "IEXPLORER";
                string processToStartOrKill = "IExplore.exe";
                GetBrowserType(ref processName, ref processToStartOrKill);
                ProcessStartInfo startInfo = new ProcessStartInfo(processToStartOrKill);
                startInfo.Arguments = url;
                Process.Start(startInfo);
                returnValue = true;
            }
            catch (Win32Exception win32EX)
            {
                returnValue = true;
            }
            catch
            {
                returnValue = false;
            }
            return returnValue;
        }
    }
}
